package com.taotao.complier.calcu.lex;


import static com.taotao.complier.calcu.lex.CharTypes.EOF;

/**
 * 分词器：输入代码，输出token流（token流由Parser分析）
 * <pre>
 *     Lexer lex = new Lexer("code");
 *     while(lex.nextToken()){
 *         Token token = lex.token();
 *         System.out.println(token);
 *     }
 * </pre>
 *
 * @author fanbeibei
 * @date 2020/9/5 13:39
 */
public class Lexer {
    /**
     * 代码
     */
    public final String text;
    public final int textLen;

    /**
     * 当前解析到的行
     */
    protected int line = 0;
    /**
     * 当前行解析到的位置
     */
    protected int pos = -1;
    /**
     * 当前解析到的字符
     */
    protected char ch = ' ';
    /**
     * token开始的位置
     */
    private int startPos = 0;


    /**
     * 当前解析到的token
     */
    protected Token token;


    public Lexer(String text) {
        this.text = text;
        this.textLen = text.length();
    }

    /**
     * 向前读一个字符
     */
    protected final void scanChar() {
        ch = charAt(++pos);
    }

    /**
     * Return the current token's position: a 0-based offset from beginning of the raw input stream (before unicode
     * translation)
     */
    public final int pos() {
        return pos;
    }

    /**
     * Return the current token, set by nextToken().
     */
    public final Token token() {
        return token;
    }

    /**
     * 继续解析到下一个token
     *
     * @return 是否可以继续解析
     */
    public boolean nextToken() {
        //  核心分词逻辑
        if (isEOF()) {
            return false;
        }


        while (!isEOF()) {
            scanChar();


            switch (ch) {
                case '+':
                    token = new Token("+", WordType.ADD);
                    startPos = pos;
                    return true;
                case '-':
                    token = new Token("-", WordType.SUB);
                    startPos = pos;
                    return true;
                case '*':
                    token = new Token("*", WordType.MUL);
                    startPos = pos;
                    return true;
                case '/':
                    token = new Token("/", WordType.DIV);
                    startPos = pos;
                    return true;
                default:
                    if (Character.isDigit(ch)) {
                        // 读到数字则继续向前读
                        startPos = pos;
                        while (!isEOF()) {
                            scanChar();
                            // 下一个是数字
                            if (Character.isDigit(ch)) {
                                continue;
                            }

                            // 下一个不是数字
                            if (Character.isWhitespace(ch) || isOperator(ch) || isEOF()) {
                                token = new Token(text.substring(startPos, pos), WordType.NUMBER);
                                return true;
                            }


                            throw new IllegalStateException("error  char :" + charAt(pos) + ",at line " + line + ",pos:" + pos);
                        }


                    } else if (Character.isWhitespace(ch)) {
                        // 空白字符跳过
                        continue;
                    } else if (isEOF()) {
                        token = new Token("", WordType.EOF);
                        // 到文件结尾
                        return false;
                    } else {
                        throw new IllegalStateException("error  char :" + charAt(pos) + ",at line " + line + ",pos:" + pos);
                    }

            }


        }


        return true;
    }

    private boolean isOperator(char ch) {
        return ch == '+' || ch == '-' || ch == '*' || ch == '/';
    }


    /**
     * 当前是否输入结束符
     *
     * @return
     */
    private boolean isEOF() {
        // java字符串结尾可能会带 \0x1A 字符
        return pos >= textLen || EOF == ch;
    }


    private char charAt(int index) {
        if (index >= textLen) {
            return EOF;
        }

        return text.charAt(index);
    }




    /*  --------------------------------以下scanXX方法是解析出各种别码的token----------------------------------------------------------------- */

    /**
     * 解析出标识符
     */
    public void scanIdentifier() {

    }


}
